﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using BLL.Extensions;

namespace Web.Infrastructure.JSExtensions
{
    public static class JsExtensions
    {
        public static MvcHtmlString JsCheckBox(this HtmlHelper htmlHelper, string name)
        {
            return JsCheckBox(htmlHelper, name, (object)null /* htmlAttributes */);
        }

        public static MvcHtmlString EnumDropDown(this HtmlHelper htmlHelper, object enumValue, string emptyItemValue, string name, string attributes)
        {
            Array values = Enum.GetValues(enumValue.GetType());

            var retVal = new List<SelectListItem>();
            if (!string.IsNullOrEmpty(emptyItemValue))
            {
                var item = new SelectListItem();
                item.Text = emptyItemValue;
                item.Value = string.Empty;
                retVal.Add(item);
            }

            foreach (var value in values)
            {
                var item = new SelectListItem();
                item.Text = ((Enum)value).GetDescription();
                item.Value = (Convert.ToInt32(value)).ToString();
                retVal.Add(item);
            }

            return JsDropDown(htmlHelper, retVal, name, attributes);
        }

        public static MvcHtmlString JsDropDown(this HtmlHelper htmlHelper, IEnumerable<SelectListItem> items, string name, string attributes)
        {
            var result = new StringBuilder();
            result.Append(string.Format("<select Id=\"{0}\" name=\"{0}\" {1}>", name, attributes));
            foreach (var selectListItem in items)
            {
                result.AppendFormat("<option <%={2}=={0}?'selected=\"selected\"':''%> value=\"{0}\" >{1}</option>", selectListItem.Value, selectListItem.Text, name);
            }
            result.Append("</select>");
            return new MvcHtmlString(result.ToString());
        }

        public static MvcHtmlString SortColumn(this HtmlHelper htmlHelper,string columnName, string name)
        {
            var result = string.Format("<th <%=((Direction==0 && Column==\"{0}\")?'class=\"sort_asc\"':'class=\"sort_desc\"')%>>", columnName);
            result = result +
                     string.Format(
                         "<a column=\"{0}\" class=\"no-link\" direction=\"<%=Direction==0?'1':'0'%>\" href=\"#\">{1}</a></th>",
                         columnName, name);
            return MvcHtmlString.Create(result);
        }

        public static MvcHtmlString Paginator(this HtmlHelper htmlHelper, int colspan, string url)
        {
            var result = new StringBuilder();
            result.Append(string.Format(
                "<tr><td class=\"pager\" colspan=\"{0}\"><span class=\"current\">Page <%=Page%> of <%=TotalPages%>.",
                colspan));
            result.Append("</span><label>Items per page: </label><select class=\"page-size\" name=\"page-size\">");
            result.Append("<option <%=Size==10?'selected=\"selected\"':''%> value=\"10\">10</option>");
            result.Append("<option <%=Size==20?'selected=\"selected\"':''%> value=\"20\">20</option>");
            result.Append("<option <%=Size==50?'selected=\"selected\"':''%> value=\"50\">50</option></select>");
            result.Append("<div class=\"pagination\"><%=Page>1?'<a data-href=\"1\">First</a>':''%>");
            result.Append("<% if(Page>1){%><a data-href=\"<%=(Page-1)%>\">Previous</a><%} else {%>");
            result.Append("<% if(Page=1){%>");
            result.Append("<span class=\"first-link\">First</span>");
            result.Append("<span class=\"first-link\">Previous</span>");
            result.Append("<%}%>");
            result.Append("<%}%>");
            result.Append("<% if(TotalPages>1){%>");
            result.Append("<% for (var index = Page-2; index < Page+3; index++){ %>");
            result.Append("<% if (index>0 && index<=TotalPages) { %>");
            result.Append("<% if(index!=Page){ %>");
            result.Append("<a class=\"no-link\" data-href=\"<%=index%>\"><%=index%></a>");
            result.Append("<%} else {%>");
            result.Append("<a data-href='<%=index%>' class=\"current no-link\"><%=index%></a>");
            result.Append("<%}%>");
            result.Append("<% } %>");
            result.Append("<% } %>");
            result.Append("<% if(Page<TotalPages) {%>");
            result.Append("<a data-href=\"<%=(Page+1)%>\">Next</a><a class=\"no-link\" data-href=\"<%=TotalPages%>\">Last</a> <%} else{%>");
            result.Append("<% if(Page=TotalPages){%>");
            result.Append("<span class=\"last-link no-link\">Next</span>");
            result.Append("<span class=\"last-link no-link\">Last</span>");
            result.Append("<%}%>");
            result.Append("<%}%>");
            result.Append("<%} else {%>");
            result.Append("<a class=\"current\">1</a>");
            result.Append("<span class=\"last-link no-link\">Next</span>");
            result.Append("<span class=\"last-link no-link\">Last</span>");
            result.Append("<%}%>");
            result.Append("</div></td></tr>");
            return MvcHtmlString.Create(HttpUtility.HtmlDecode(result.ToString()));
        }

        public static MvcHtmlString JsCheckBox(this HtmlHelper htmlHelper, string name, object htmlAttributes)
        {
            return JsCheckBox(htmlHelper, name, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
        }

        public static MvcHtmlString JsCheckBox(
            this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes)
        {
            return CheckBoxHelper(htmlHelper, null, name, htmlAttributes);
        }

        public static MvcHtmlString JsHidden(this HtmlHelper htmlHelper, string name)
        {
            return JsHidden(htmlHelper, name, null /* htmlAttributes */);
        }

        public static MvcHtmlString JsHidden(
            this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes)
        {
            return HiddenHelper(
                htmlHelper,
                null,
                false,
                name,
                htmlAttributes);
        }

        public static MvcHtmlString JsPassword(this HtmlHelper htmlHelper, string name)
        {
            return JsPassword(htmlHelper, name, null);
        }

        public static MvcHtmlString JsPassword(
            this HtmlHelper htmlHelper, string name, object htmlAttributes)
        {
            return JsPassword(
                htmlHelper, name, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
        }

        public static MvcHtmlString JsPassword(
            this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes)
        {
            return PasswordHelper(htmlHelper, null /* metadata */, name, htmlAttributes);
        }

        public static MvcHtmlString JsTextBox(this HtmlHelper htmlHelper, string name)
        {
            return JsTextBox(htmlHelper, name, null);
        }

        public static MvcHtmlString JsTextBox(
            this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes)
        {
            return InputHelper(
                htmlHelper,
                InputType.Text,
                null,
                name,
                true /* useViewData */,
                false /* isChecked */,
                true /* setId */,
                htmlAttributes);
        }


        public static MvcHtmlString JsTextArea(this HtmlHelper htmlHelper, string name)
        {
            return JsTextArea(htmlHelper, name,  null);
        }

        public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, object htmlAttributes)
        {
            return TextAreaHelper(htmlHelper,  name, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
        }

        public static MvcHtmlString JsTextArea(this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes)
        {
            return TextAreaHelper(htmlHelper, name, htmlAttributes);
        }

        private static MvcHtmlString TextAreaHelper(HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes)
        {
            string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);

            var tagBuilder = new TagBuilder("textarea");
            tagBuilder.GenerateId(fullName);
            tagBuilder.MergeAttributes(htmlAttributes, true);
            tagBuilder.MergeAttribute("name", fullName, true);

            // If there are any errors for a named field, we add the CSS attribute.
            ModelState modelState;
            if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState) && modelState.Errors.Count > 0)
            {
                tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
            }

            tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name));

            string value = "{{" + fullName + "}}";

            // The first newline is always trimmed when a TextArea is rendered, so we add an extra one
            // in case the value being rendered is something like "\r\nHello".
            tagBuilder.SetInnerText(Environment.NewLine + value);

            return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
        }

        private static MvcHtmlString CheckBoxHelper(
            HtmlHelper htmlHelper,
            ModelMetadata metadata,
            string name,
            IDictionary<string, object> htmlAttributes)
        {
            RouteValueDictionary attributes = ToRouteValueDictionary(htmlAttributes);

            return InputHelper(
                htmlHelper,
                InputType.CheckBox,
                metadata,
                name,
                true,
                true,
                true,
                attributes);
        }

        private static MvcHtmlString PasswordHelper(
            HtmlHelper htmlHelper,
            ModelMetadata metadata,
            string name,
            IDictionary<string, object> htmlAttributes)
        {
            return InputHelper(
                htmlHelper,
                InputType.Password,
                metadata,
                name,
                false /* useViewData */,
                false /* isChecked */,
                true /* setId */,
                htmlAttributes);
        }

        private static MvcHtmlString HiddenHelper(
            HtmlHelper htmlHelper,
            ModelMetadata metadata,
            bool useViewData,
            string expression,
            IDictionary<string, object> htmlAttributes)
        {
            return InputHelper(
                htmlHelper,
                InputType.Hidden,
                metadata,
                expression,
                useViewData,
                false /* isChecked */,
                true /* setId */,
                htmlAttributes);
        }

        private static MvcHtmlString InputHelper(
            HtmlHelper htmlHelper,
            InputType inputType,
            ModelMetadata metadata,
            string name,
            bool useViewData,
            bool isChecked,
            bool setId,
            IDictionary<string, object> htmlAttributes)
        {
            string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
            var tagBuilder = new TagBuilder("input");
            tagBuilder.MergeAttributes(htmlAttributes);
            tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
            tagBuilder.MergeAttribute("name", fullName, true);
            tagBuilder.MergeAttribute("placeholder", htmlHelper.ViewContext.ViewData.ModelMetadata.DisplayName ??  htmlHelper.ViewContext.ViewData.ModelMetadata.PropertyName, true);
            string value = "model." + fullName;
            bool usedModelState = false;

            switch (inputType)
            {
                case InputType.CheckBox:
                    tagBuilder.MergeAttribute("type","checkbox");
                    tagBuilder.MergeAttribute("ng-model", value);
                    tagBuilder.MergeAttribute("ng-checked", value);
                    break;
                case InputType.Radio:
                    tagBuilder.MergeAttribute("type", "radio");
                    tagBuilder.MergeAttribute("ng-checked", value);
                    tagBuilder.MergeAttribute("ng-model", value);
                    break;
                case InputType.Password:
                    tagBuilder.MergeAttribute("ng-model", value);
                    break;
                default:
                    tagBuilder.MergeAttribute("ng-model", value);
                    break;
            }

            if (setId)
                tagBuilder.GenerateId(fullName);

            // If there are any errors for a named field, we add the css attribute.
            ModelState modelState;
            if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
            {
                if (modelState.Errors.Count > 0)
                    tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
            }

            tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));

            return
                MvcHtmlString.Create(
                    tagBuilder.ToString(TagRenderMode.SelfClosing));
        }

        private static RouteValueDictionary ToRouteValueDictionary(IDictionary<string, object> dictionary)
        {
            return dictionary == null ? new RouteValueDictionary() : new RouteValueDictionary(dictionary);
        }

        private static object GetModelStateValue(string key, Type destinationType, HtmlHelper htmlHelper)
        {
            ModelState modelState;
            if (htmlHelper.ViewData.ModelState.TryGetValue(key, out modelState))
            {
                if (modelState.Value != null)
                    return modelState.Value.ConvertTo(destinationType, null /* culture */);
            }
            return null;
        }

        private static bool EvalBoolean(string key, HtmlHelper htmlHelper)
        {
            return Convert.ToBoolean(htmlHelper.ViewData.Eval(key), CultureInfo.InvariantCulture);
        }
    }
}
